/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package spe.ui.editor;

import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.StringTokenizer;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.jbpm.gd.common.Logger;
import org.jbpm.gd.common.editor.AbstractContentProvider;
import org.jbpm.gd.common.editor.ContentProvider;
import org.jbpm.gd.common.model.NamedElement;
import org.jbpm.gd.common.model.SemanticElement;
import org.jbpm.gd.common.notation.Edge;
import org.jbpm.gd.common.notation.Node;
import org.jbpm.gd.common.notation.NodeContainer;
import org.jbpm.gd.common.notation.NotationElement;
import org.jbpm.gd.common.notation.NotationMapping;
import org.jbpm.gd.common.notation.RootContainer;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import spe.model.spem.MethodConfiguration;
import spe.model.spem.MethodContentPackage;
import spe.model.spem.MethodLibrary;
import spe.model.spem.MethodPlugin;
import spe.model.spem.PackageElement;
import spe.model.spem.ProcessPackage;
import spe.model.spem.SPEMElement;

public class SPEMContentProvider
{
	private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
	private static TransformerFactory transformerFactory = TransformerFactory.newInstance();
	private Document document;
	
	protected String createInitialNotationInfo() {
		StringBuffer buffer = new StringBuffer();
		buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
		buffer.append("\n\n");
		buffer.append("<spe-container/>");
		return buffer.toString();
	}

	
	protected String getNotationInfoFileName(String semanticInfoFileName)
	{
		if ("spe-model.xml".equals(semanticInfoFileName)) 
		{
			return "spegd.xml";
		} else {
			return ".spegd." + semanticInfoFileName;
		}
	}

	protected String getSemanticInfoFileName(String notationInfoFileName)
	{
		if ("spegd.xml".equals(notationInfoFileName)) {
			return "spe-model.xml";
		} else {
			return notationInfoFileName.substring(13);
		}
	}
	
	protected void addNodes(MethodLibrary methodLibrary)
	{
		
	}
	
	protected void addMethodConfiguration(MethodConfiguration mc, Element parentNodeElement)
	{
		if( (parentNodeElement == null) || !(parentNodeElement.getNodeName().equals("methodConfigurations")))
			throw new IllegalArgumentException("Cannot add new element here!");
		
		Element newElement = document.createElement("MethodConfiguration");
		newElement.setAttribute("name", mc.getName());
		newElement.setAttribute("id", mc.getId());
		
		parentNodeElement.appendChild(newElement);
	}
	
	protected void addMethodPlugin(MethodPlugin mp, Element parentNodeElement)
	{
		if( (parentNodeElement == null) || !(parentNodeElement.getNodeName().equals("methodPlugins")))
			throw new IllegalArgumentException("Cannot add new element here!");
		
		Element newElement = document.createElement("MethodPlugin");
		newElement.setAttribute("name", mp.getName());
		newElement.setAttribute("id", mp.getId());
		
		for(SPEMElement child: mp.getChildElements())
		{
			if(child instanceof MethodContentPackage)
				addMethodContentPackage((MethodContentPackage)child, newElement);
			
			if(child instanceof ProcessPackage)
				addProcessPackage((ProcessPackage)child, newElement);
		}
		
		parentNodeElement.appendChild(newElement);
	}
	
	private void addMethodContentPackage(MethodContentPackage mcp, Element pElement)
	{
		
	}
	
	private void addProcessPackage(ProcessPackage pp, Element pElement)
	{
		
	}
	
	/**
	 * 	Get child elements
	 * 	Params:
	 * 			notationElement The element we want to get children from.
	 * 
	 * */
	private ArrayList<Element> getNodeElements(Element notationElement) 
	{
		ArrayList<Element> result = new ArrayList<Element>();
		NodeList nodeList = notationElement.getChildNodes();
		for (int i = 0; i < nodeList.getLength(); i++) 
		{
			org.w3c.dom.Node node = nodeList.item(i);
			if (!(node instanceof Element)) continue;
			Element element = (Element)node;
			if ("node".equals(element.getNodeName()) || "node-container".equals(element.getNodeName()))
				result.add(element);
		}
		return result;
	}
	
	private String getAttribute(Element element, String name)
	{
		return element.hasAttribute(name) ? element.getAttribute(name) : null;
	}
	
	private void createNotationInfoFile(IFile notationInfoFile) 
	{
		try {
			notationInfoFile.create(new ByteArrayInputStream(createInitialNotationInfo().toString().getBytes()), true, null);
		} catch (CoreException e) {
			Logger.logError(e);
		}
	}
	
	private IFile getNotationInfoFile(IFile semanticInfoFile)
	{
		IProject project = semanticInfoFile.getProject();
		IPath semanticInfoPath = semanticInfoFile.getProjectRelativePath();
		IPath notationInfoPath = semanticInfoPath.removeLastSegments(1).append(getNotationInfoFileName(semanticInfoFile.getName()));
		IFile notationInfoFile = project.getFile(notationInfoPath);
		if (!notationInfoFile.exists())
		{
			createNotationInfoFile(notationInfoFile);
		}
		return notationInfoFile;
	}

	public boolean saveToInput(IEditorInput input, MethodLibrary methodLibrary) 
	{
		boolean result = true;
		try {
			IFile file = getNotationInfoFile(((IFileEditorInput)input).getFile());
			//Element notationInfo = documentBuilderFactory.newDocumentBuilder().parse(file.getContents()).getDocumentElement();
			//if (upToDateCheck(notationInfo)) 
			//{
			getNotationInfoFile(file).setContents(new ByteArrayInputStream(toNotationInfoXml(methodLibrary).getBytes()), true, true, null);
			//} else {
				//result = false;
			//}
		} catch (Exception e) {
			Logger.logError("Problem while saving the input.", e);
		}
		return result;
	}

	public void addNotationInfo(MethodLibrary methodLibrary, IEditorInput input)
	{
		
	}
	
	private String toNotationInfoXml(MethodLibrary rootContainer) 
	{
		StringWriter writer = new StringWriter();
		write(rootContainer, writer);
		return writer.toString();
	}
	
	private void write(	MethodLibrary rootContainer, Writer writer) 
	{
		try {
			//Document document = documentBuilderFactory.newDocumentBuilder().newDocument();
			Element root = document.createElement("spe-container");
			document.appendChild(root);
			write("\n	", rootContainer, root);
			DOMSource domSource = new DOMSource(root);
			Transformer transformer = transformerFactory.newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			StreamResult streamResult = new StreamResult(writer);
			transformer.transform(domSource, streamResult);
//			transformer.transform(domSource, new StreamResult(System.out));
		} catch (Exception e) {
			Logger.logError("Problem while saving to disk", e);
		}
	}
	
	private void write(String indent, SPEMElement e, Element element) 
	{
		/*
		addAttribute(element, "name", e.getName());
		addAttribute(element, "x", Integer.toString(e.getPosition().x));
		addAttribute(element, "y", Integer.toString(e.getPosition().y));
		
		if(e instanceof PackageElement)
		{
			Iterator<SPEMElement> iter = ((PackageElement)e).getChildElements().iterator();
			while (iter.hasNext())
			{
				write("\n  ", iter.next(), element);
			}
		}
		*/
	}
	
	
	
	private void addAttribute(Element e, String attributeName, String value) {
		if (value != null) {
			Attr attr = e.getOwnerDocument().createAttribute(attributeName);
			attr.setNodeValue(value);
			e.setAttributeNode(attr);
		}
	}
	
}
